﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using DSS_XNA.Engine.Error;
using DSS_XNA.Engine.Log;
using Microsoft.Xna.Framework.Input;
using System.Xml;
using System.Reflection;

namespace DSS_XNA.Engine.World
{
    public class World
    {
        #region Properties

        /// <summary>
        /// A meter in pixels
        /// </summary>
        public static float Meter = 100.0f;

        public static float Gravity = 1.0f;

        public Game1 Engine
        {
            get { return _engine; }
            set { _engine = value; }
        }
        private Game1 _engine = null;

        public IWorld CurrentWorld
        {
            get { return _currentWorld; }
            set { _currentWorld = value; }
        }
        private IWorld _currentWorld = null;

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        private string _name = String.Empty;

        #endregion

        #region Public Methods

        public World(string filePath, Game1 engine)
        {
            if (filePath == null)
                throw new DSSException("'filePath' is null.");
            if (filePath.Length == 0)
                throw new DSSException("'filePath' is zero-length.");
            if (engine == null)
                throw new DSSException("'filePath' is null.");

            this.Load(filePath, engine);
        }

        public void Load(string filePath, Game1 engine)
        {
            if (filePath == null)
                throw new DSSException("'filePath' is null.");
            if (filePath.Length == 0)
                throw new DSSException("'filePath' is zero-length.");
            if (engine == null)
                throw new DSSException("'filePath' is null.");

            Logger.Log(String.Format("Loading world: {0}", filePath), LogLevel.Major);

            this.Engine = engine;

            this.Destroy();

            this.LoadFromFile(filePath);
        }

        public void Render(SpriteBatch spriteBatch)
        {
            if (this.CurrentWorld != null)
            {
                this.CurrentWorld.Render(spriteBatch);
            }
        }

        public void Update(GameTime gameTime, KeyboardState ks, MouseState ms, GamePadState gs)
        {
            if (this.CurrentWorld != null)
            {
                this.CurrentWorld.Update(gameTime, ks, ms, gs);
            }
        }

        public void Destroy()
        {
        }

        #endregion

        #region Private Methods

        private void LoadFromFile(string filePath)
        {
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(filePath);

                this._name = doc.SelectSingleNode("level/name").InnerText;

                string className = doc.SelectSingleNode("level/class").InnerText;
                string worldFile = doc.SelectSingleNode("level/worldFile").InnerText;

                worldFile = String.Format("worlds\\{0}.wld", worldFile);
                worldFile = Util.Util.GetFullPath(worldFile);

                this.LoadWorld(className, worldFile);
            }
            catch (Exception ex)
            {
                Logger.Log(String.Format("Failed to load World: {0}\n\n{1}",
                    filePath, ex), LogLevel.Major);
            }
        }

        private void LoadWorld(string className, string filePath)
        {
            if (className == null)
                throw new ArgumentNullException("'className' is null.");
            if (className.Length == 0)
                throw new ArgumentOutOfRangeException("'className' is zero-length.");
            if (filePath == null)
                throw new ArgumentNullException("'filePath' is null.");
            if (filePath.Length == 0)
                throw new ArgumentOutOfRangeException("'filePath' is zero-length.");

            try
            {
                Assembly asm = Assembly.GetCallingAssembly();
                Type type = asm.GetType(className);
                if (type == null)
                    throw new DSSException(String.Format("Failed to find the world class: {0}",
                        className));

                if (type.GetInterface(typeof(IWorld).FullName) == null)
                    throw new DSSException(String.Format("The world class: {0} does not inherit from {1}",
                        className, typeof(IWorld)));

                ConstructorInfo con = type.GetConstructor(new Type[] { typeof(string), typeof(Game1) });

                this._currentWorld = con.Invoke(new object[] { filePath, this.Engine }) as IWorld;
                if (this._currentWorld == null)
                    throw new DSSException("'this._currentWorld' is null.");
            }
            catch (Exception ex)
            {
                Logger.LogCritical(String.Format("Failed to load menu class: {0}\n\n{1}",
                    className, ex));
            }
        }

        #endregion
    }
}
